μ¬μ©μ μ μ λ°μ½λ μ΄ν°λ₯Ό μ¬μ©νμ¬ Flask μΉ μ ν리μΌμ΄μ μ 보νΈνλ λ°©λ²μ μμ보μΈμ. μμ ν API λ° μΉ μΈν°νμ΄μ€ ꡬμΆμ μν μ€μ©μ μΈ μμ, λͺ¨λ² μ¬λ‘ λ° κΈλ‘λ² κ³ λ € μ¬νμ μ΄ν΄λ³΄μΈμ.
Flask μ¬μ©μ μ μ λ°μ½λ μ΄ν°: μμ ν μΉ μ ν리μΌμ΄μ μ μν λΌμ°νΈ λ³΄νΈ κ΅¬ν
μ€λλ μνΈ μ°κ²°λ μΈμμμ μμ ν μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ κ²μ λ§€μ° μ€μν©λλ€. κ°λ³κ³ λ€μ¬λ€λ₯ν Python μΉ νλ μμν¬μΈ Flaskλ κ°λ ₯νκ³ νμ₯ κ°λ₯ν μ ν리μΌμ΄μ μ λ§λ€κΈ° μν μ μ°ν νλ«νΌμ μ 곡ν©λλ€. Flask μ ν리μΌμ΄μ μ 보μμ κ°ννλ κ°λ ₯ν κΈ°μ μ€ νλλ λΌμ°νΈ 보νΈλ₯Ό μν΄ μ¬μ©μ μ μ λ°μ½λ μ΄ν°λ₯Ό μ¬μ©νλ κ²μ λλ€. μ΄ λΈλ‘κ·Έ κ²μλ¬Όμμλ μ΄λ¬ν λ°μ½λ μ΄ν°μ μ€μ©μ μΈ κ΅¬νμ λν΄ μμΈν μ€λͺ νκ³ , μμ ν API λ° μΉ μΈν°νμ΄μ€λ₯Ό ꡬμΆνκΈ° μν νμ κ°λ , μ€μ μμ λ° κΈλ‘λ² κ³ λ € μ¬νμ λ€λ£Ήλλ€.
Pythonμ λ°μ½λ μ΄ν° μ΄ν΄
Flask κ΄λ ¨ μμ λ₯Ό μ΄ν΄λ³΄κΈ° μ μ Pythonμ λ°μ½λ μ΄ν°μ λν μ΄ν΄λ₯Ό λμ겨 λ³΄κ² μ΅λλ€. λ°μ½λ μ΄ν°λ ν¨μμ λ©μλμ λμμ μμ νκ±°λ νμ₯νλ κ°λ ₯νκ³ μ°μν λ°©λ²μ λλ€. μΈμ¦, κΆν λΆμ¬, λ‘κΉ λ° μ λ ₯ μ ν¨μ± κ²μ¬μ κ°μ κ³΅ν΅ κΈ°λ₯μ μλ³Έ ν¨μμ μ½λλ₯Ό μ§μ μμ νμ§ μκ³ μ μ©νκΈ° μν κ°κ²°νκ³ μ¬μ¬μ© κ°λ₯ν λ©μ»€λμ¦μ μ 곡ν©λλ€.
λ³Έμ§μ μΌλ‘ λ°μ½λ μ΄ν°λ λ€λ₯Έ ν¨μλ₯Ό μ λ ₯μΌλ‘ λ°μ ν΄λΉ ν¨μμ μμ λ λ²μ μ λ°ννλ ν¨μμ λλ€. '@' κΈ°νΈλ λ°μ½λ μ΄ν°λ₯Ό ν¨μμ μ μ©νμ¬ μ½λλ₯Ό λ κΉλνκ³ μ½κΈ° μ½κ² λ§λλλ€. κ°λ¨ν μλ₯Ό κ³ λ €ν΄ λ³΄μΈμ.
def my_decorator(func):
def wrapper():
print("Before function call.")
func()
print("After function call.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello() # Output: Before function call. \n Hello! \n After function call.
μ΄ μμμ `my_decorator`λ `say_hello` ν¨μλ₯Ό λννλ λ°μ½λ μ΄ν°μ λλ€. `say_hello` μ€ν μ νμ κΈ°λ₯μ μΆκ°ν©λλ€. μ΄κ²μ Flaskμμ λΌμ°νΈ λ³΄νΈ λ°μ½λ μ΄ν°λ₯Ό λ§λλ κΈ°λ³Έμ μΈ κ΅¬μ± μμμ λλ€.
Flaskμμ μ¬μ©μ μ μ λΌμ°νΈ λ³΄νΈ λ°μ½λ μ΄ν° ꡬμΆ
μ¬μ©μ μ μ λ°μ½λ μ΄ν°λ₯Ό μ¬μ©ν λΌμ°νΈ 보νΈμ ν΅μ¬ μμ΄λμ΄λ μμ²μ΄ λ·° ν¨μ(λΌμ°νΈ)μ λλ¬νκΈ° μ μ μμ²μ κ°λ‘μ±λ κ²μ λλ€. λ°μ½λ μ΄ν°λ νΉμ κΈ°μ€(μ: μ¬μ©μ μΈμ¦, κΆν λΆμ¬ μμ€)μ νμΈνκ³ μμ²μ μ§ννλλ‘ νμ©νκ±°λ μ μ ν μ€λ₯ μλ΅(μ: 401 Unauthorized, 403 Forbidden)μ λ°νν©λλ€. Flaskμμ μ΄λ₯Ό ꡬννλ λ°©λ²μ μ΄ν΄λ³΄κ² μ΅λλ€.
1. μΈμ¦ λ°μ½λ μ΄ν°
μΈμ¦ λ°μ½λ μ΄ν°λ μ¬μ©μμ μ μμ νμΈνλ μν μ ν©λλ€. μΌλ°μ μΈ μΈμ¦ λ°©λ²μλ λ€μμ΄ ν¬ν¨λ©λλ€.
- κΈ°λ³Έ μΈμ¦: μμ² ν€λμ μ¬μ©μ μ΄λ¦κ³Ό μνΈ(μΌλ°μ μΌλ‘ μΈμ½λ©λ¨)λ₯Ό μ μ‘νλ κ²μ ν¬ν¨ν©λλ€. ꡬννκΈ°λ κ°λ¨νμ§λ§, νΉν μνΈνλμ§ μμ μ°κ²°μ ν΅ν΄ λ€λ₯Έ λ°©λ²λ³΄λ€ λ μμ ν κ²μΌλ‘ κ°μ£Όλ©λλ€.
- ν ν° κΈ°λ° μΈμ¦(μ: JWT): μ¬μ©μμ μ μμ νμΈνκΈ° μν΄ ν ν°(λκ° JSON μΉ ν ν° λλ JWT)μ μ¬μ©ν©λλ€. ν ν°μ μΌλ°μ μΌλ‘ μ±κ³΅μ μΈ λ‘κ·ΈμΈ ν μμ±λμ΄ νμ μμ²μ ν¬ν¨λ©λλ€(μ: `Authorization` ν€λμ). μ΄ λ°©λ²μ΄ λ μμ νκ³ νμ₯ κ°λ₯ν©λλ€.
- OAuth 2.0: μμλ κΆν λΆμ¬μ λ리 μ¬μ©λλ νμ€μ λλ€. μ¬μ©μλ μ격 μ¦λͺ μ μ§μ 곡μ νμ§ μκ³ νμ¬ μ ν리μΌμ΄μ μ μμ μ 리μμ€(μ: μμ λ―Έλμ΄ νλ«νΌμ λ°μ΄ν°)μ λν μ‘μΈμ€ κΆνμ λΆμ¬ν©λλ€.
μμ°μ μν΄ ν ν°(μ΄ κ²½μ° JWT)μ μ¬μ©νλ κΈ°λ³Έ μΈμ¦ λ°μ½λ μ΄ν°μ μλ λ€μκ³Ό κ°μ΅λλ€. μ΄ μμ μμλ JWT λΌμ΄λΈλ¬λ¦¬(μ: `PyJWT`)λ₯Ό μ¬μ©νλ€κ³ κ°μ ν©λλ€.
import functools
import jwt
from flask import request, jsonify, current_app
def token_required(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
token = None
if 'Authorization' in request.headers:
token = request.headers['Authorization'].split(' ')[1] # Extract token after 'Bearer '
if not token:
return jsonify({"message": "Token is missing!"}), 401
try:
data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
# You'll likely want to fetch user data here from a database, etc.
# For example: user = User.query.filter_by(id=data['user_id']).first()
# Then, you can pass the user object to your view function (see next example)
except jwt.ExpiredSignatureError:
return jsonify({"message": "Token has expired!"}), 401
except jwt.InvalidTokenError:
return jsonify({"message": "Token is invalid!"}), 401
return f(*args, **kwargs)
return decorated
μ€λͺ :
- `token_required(f)`: λ·° ν¨μ `f`λ₯Ό μΈμλ‘ μ¬μ©νλ λ°μ½λ μ΄ν° ν¨μμ λλ€.
- `@functools.wraps(f)`: μ΄ λ°μ½λ μ΄ν°λ μλ³Έ ν¨μμ λ©νλ°μ΄ν°(μ΄λ¦, docstring λ±)λ₯Ό μ μ§ν©λλ€.
- `decorated(*args, **kwargs)` λ΄λΆ:
- `Authorization` ν€λκ° μλμ§ νμΈνκ³ ν ν°μ μΆμΆν©λλ€("Bearer" ν ν°μ΄λΌκ³ κ°μ ).
- ν ν°μ΄ μ 곡λμ§ μμΌλ©΄ 401 Unauthorized μ€λ₯λ₯Ό λ°νν©λλ€.
- Flask μ ν리μΌμ΄μ μ ꡬμ±μμ `SECRET_KEY`λ₯Ό μ¬μ©νμ¬ JWTλ₯Ό λμ½λ©μ μλν©λλ€. `SECRET_KEY`λ μμ νκ² μ μ₯ν΄μΌ νλ©° μ½λμ μ§μ μ μ₯ν΄μλ μ λ©λλ€.
- ν ν°μ΄ μ ν¨νμ§ μκ±°λ λ§λ£λ κ²½μ° 401 μ€λ₯λ₯Ό λ°νν©λλ€.
- ν ν°μ΄ μ ν¨νλ©΄ λͺ¨λ μΈμλ₯Ό μ¬μ©νμ¬ μλ³Έ λ·° ν¨μ `f`λ₯Ό μ€νν©λλ€. λμ½λ©λ `data` λλ μ¬μ©μ κ°μ²΄λ₯Ό λ·° ν¨μμ μ λ¬ν μ μμ΅λλ€.
μ¬μ© λ°©λ²:
from flask import Flask, jsonify
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
@app.route('/protected')
@token_required
def protected_route():
return jsonify({"message": "This is a protected route!"}), 200
`/protected` λΌμ°νΈμ μ‘μΈμ€νλ €λ©΄ `Authorization` ν€λμ μ ν¨ν JWTλ₯Ό ν¬ν¨ν΄μΌ ν©λλ€(μ: `Authorization: Bearer
2. κΆν λΆμ¬ λ°μ½λ μ΄ν°
κΆν λΆμ¬ λ°μ½λ μ΄ν°λ μΈμ¦μ κΈ°λ°μΌλ‘ νλ©° μ¬μ©μκ° νΉμ 리μμ€μ μ‘μΈμ€νλ λ° νμν κΆνμ΄ μλμ§ νμΈν©λλ€. μΌλ°μ μΌλ‘ μ΄λ 미리 μ μλ κ·μΉ μ§ν©μ λν μ¬μ©μ μν λλ κΆνμ νμΈνλ κ²μ ν¬ν¨ν©λλ€. μλ₯Ό λ€μ΄ κ΄λ¦¬μλ λͺ¨λ 리μμ€μ μ‘μΈμ€ν μ μμ§λ§ μΌλ° μ¬μ©μλ μμ μ λ°μ΄ν°μλ§ μ‘μΈμ€ν μ μμ΅λλ€.
νΉμ μ¬μ©μ μν μ νμΈνλ κΆν λΆμ¬ λ°μ½λ μ΄ν°μ μλ λ€μκ³Ό κ°μ΅λλ€.
import functools
from flask import request, jsonify, current_app
def role_required(role):
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
# Assuming you have a way to get the user object
# For example, if you're using the token_required decorator
# and passing the user object to the view function:
try:
user = request.user # Assume you've set the user object in a previous decorator
except AttributeError:
return jsonify({"message": "User not authenticated!"}), 401
if not user or user.role != role:
return jsonify({"message": "Insufficient permissions!"}), 403
return f(*args, **kwargs)
return wrapper
return decorator
μ€λͺ :
- `role_required(role)`: νμν μν (μ: 'admin', 'editor')μ μΈμλ‘ μ¬μ©νλ λ°μ½λ μ΄ν° ν©ν 리μ λλ€.
- `decorator(f)`: λ·° ν¨μ `f`λ₯Ό μΈμλ‘ μ¬μ©νλ μ€μ λ°μ½λ μ΄ν°μ λλ€.
- `@functools.wraps(f)`: μλ³Έ ν¨μμ λ©νλ°μ΄ν°λ₯Ό 보쑴ν©λλ€.
- `wrapper(*args, **kwargs)` λ΄λΆ:
- μ¬μ©μ κ°μ²΄λ₯Ό κ²μν©λλ€( `token_required` λ°μ½λ μ΄ν° λλ μ μ¬ν μΈμ¦ λ©μ»€λμ¦μΌλ‘ μ€μ λμλ€κ³ κ°μ ). μ΄λ ν ν°μμ μΆμΆν μ¬μ©μ μ 보λ₯Ό κΈ°λ°μΌλ‘ λ°μ΄ν°λ² μ΄μ€μμ λ‘λν μλ μμ΅λλ€.
- μ¬μ©μκ° μλμ§, κ·Έλ¦¬κ³ ν΄λΉ μν μ΄ νμν μν κ³Ό μΌμΉνλμ§ νμΈν©λλ€.
- μ¬μ©μκ° κΈ°μ€μ μΆ©μ‘±νμ§ λͺ»νλ©΄ 403 Forbidden μ€λ₯λ₯Ό λ°νν©λλ€.
- μ¬μ©μκ° κΆνμ λΆμ¬λ°μΌλ©΄ μλ³Έ λ·° ν¨μ `f`λ₯Ό μ€νν©λλ€.
μ¬μ© λ°©λ²:
from flask import Flask, jsonify
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
# Assume the token_required decorator sets request.user (as described above)
@app.route('/admin')
@token_required # Apply authentication first
@role_required('admin') # Then, apply authorization
def admin_route():
return jsonify({"message": "Welcome, admin!"}), 200
μ΄ μμ μμ `/admin` λΌμ°νΈλ `token_required`(μΈμ¦) λ° `role_required('admin')`(κΆν λΆμ¬) λ°μ½λ μ΄ν°λ‘ 보νΈλ©λλ€. 'admin' μν μ΄ μλ μΈμ¦λ μ¬μ©μλ§ μ΄ λΌμ°νΈμ μ‘μΈμ€ν μ μμ΅λλ€.
κ³ κΈ κΈ°μ λ° κ³ λ € μ¬ν
1. λ°μ½λ μ΄ν° 체μ΄λ
μμ μ€λͺ λ κ²μ²λΌ λ°μ½λ μ΄ν°λ₯Ό 체μ΄λνμ¬ μ¬λ¬ μμ€μ 보νΈλ₯Ό μ μ©ν μ μμ΅λλ€. μΈμ¦μ μΌλ°μ μΌλ‘ 체μΈμμ κΆν λΆμ¬ μ μ μνλμ΄μΌ ν©λλ€. μ΄λ κ² νλ©΄ κΆν λΆμ¬ μμ€μ νμΈνκΈ° μ μ μ¬μ©μκ° μΈμ¦λμλμ§ νμΈν©λλ€.
2. λ€μν μΈμ¦ λ°©λ² μ²λ¦¬
μμ© νλ‘κ·Έλ¨μ μꡬ μ¬νμ λ°λΌ OAuth 2.0 λλ κΈ°λ³Έ μΈμ¦κ³Ό κ°μ λ€μν μΈμ¦ λ°©λ²μ μ§μνλλ‘ μΈμ¦ λ°μ½λ μ΄ν°λ₯Ό μ‘°μ νμμμ€. μ¬μ©ν μΈμ¦ λ°©λ²μ κ²°μ νκΈ° μν΄ κ΅¬μ± κ°λ₯ν μ κ·Ό λ°©μμ μ¬μ©νλ κ²μ κ³ λ €νμμμ€.
3. 컨ν μ€νΈ λ° λ°μ΄ν° μ λ¬
λ°μ½λ μ΄ν°λ λ·° ν¨μμ λ°μ΄ν°λ₯Ό μ λ¬ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ μΈμ¦ λ°μ½λ μ΄ν°λ JWTλ₯Ό λμ½λ©νκ³ μ¬μ©μ κ°μ²΄λ₯Ό λ·° ν¨μμ μ λ¬ν μ μμ΅λλ€. μ΄λ κ² νλ©΄ λ·° ν¨μ λ΄μμ μΈμ¦ λλ λ°μ΄ν° κ²μ μ½λλ₯Ό λ°λ³΅ν νμκ° μμ΅λλ€. λ°μ½λ μ΄ν°κ° μκΈ°μΉ μμ λμμ λ°©μ§νκΈ° μν΄ λ°μ΄ν°λ₯Ό μ μ νκ² μ λ¬νλμ§ νμΈνμμμ€.
4. μ€λ₯ μ²λ¦¬ λ° λ³΄κ³
λ°μ½λ μ΄ν°μμ ν¬κ΄μ μΈ μ€λ₯ μ²λ¦¬λ₯Ό ꡬνν©λλ€. μ€λ₯λ₯Ό κΈ°λ‘νκ³ , μ μ΅ν μ€λ₯ μλ΅μ λ°ννκ³ , λ¬Έμ λͺ¨λν°λ§ λ° μΆμ μ μν΄ μ μ© μ€λ₯ λ³΄κ³ λ©μ»€λμ¦(μ: Sentry)μ μ¬μ©νλ κ²μ κ³ λ €νμμμ€. μ΅μ’ μ¬μ©μμκ² μ μ©ν λ©μμ§(μ: μλͺ»λ ν ν°, κΆν λΆμ‘±)λ₯Ό μ 곡νλ λμμ λ―Όκ°ν μ 보 λ ΈμΆμ νΌνμμμ€.
5. μλ μ ν
APIλ₯Ό λ¨μ© λ° μλΉμ€ κ±°λΆ(DoS) 곡격μΌλ‘λΆν° 보νΈνκΈ° μν΄ μλ μ νμ ν΅ν©ν©λλ€. μ£Όμ΄μ§ μκ° λ΄μ νΉμ IP μ£Όμ λλ μ¬μ©μμ μμ² μλ₯Ό μΆμ νκ³ μμ² μλ₯Ό μ ννλ λ°μ½λ μ΄ν°λ₯Ό λ§λλλ€. λ°μ΄ν°λ² μ΄μ€, μΊμ(Redisμ μ μ¬) λλ κΈ°ν μμ μ μΈ μ루μ μ μ¬μ©μ ꡬνν©λλ€.
import functools
from flask import request, jsonify, current_app
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
# Initialize Limiter (ensure this is done during app setup)
limiter = Limiter(
app=current_app._get_current_object(),
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
def rate_limit(limit):
def decorator(f):
@functools.wraps(f)
@limiter.limit(limit)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decorator
# Example usage
@app.route('/api/resource')
@rate_limit("10 per minute")
def api_resource():
return jsonify({"message": "API resource"})
6. μ λ ₯ μ ν¨μ± κ²μ¬
λ°μ½λ μ΄ν° λ΄μμ μ¬μ©μ μ λ ₯μ κ²μ¬νμ¬ ν¬λ‘μ€ μ¬μ΄νΈ μ€ν¬λ¦½ν (XSS) λ° SQL μ£Όμ κ³Ό κ°μ μΌλ°μ μΈ μ·¨μ½μ±μ λ°©μ§ν©λλ€. Marshmallow λλ Pydanticκ³Ό κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ¬ λ°μ΄ν° μ€ν€λ§λ₯Ό μ μνκ³ λ€μ΄μ€λ μμ² λ°μ΄ν°λ₯Ό μλμΌλ‘ κ²μ¬ν©λλ€. λ°μ΄ν° μ²λ¦¬ μ μ ν¬κ΄μ μΈ κ²μ¬λ₯Ό ꡬνν©λλ€.
from functools import wraps
from flask import request, jsonify
from marshmallow import Schema, fields, ValidationError
# Define a schema for input validation
class UserSchema(Schema):
email = fields.Email(required=True)
password = fields.Str(required=True, min_length=8)
def validate_input(schema):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
try:
data = schema.load(request.get_json())
except ValidationError as err:
return jsonify(err.messages), 400
request.validated_data = data # Store validated data in the request object
return f(*args, **kwargs)
return wrapper
return decorator
# Example Usage
@app.route('/register', methods=['POST'])
@validate_input(UserSchema())
def register_user():
# Access validated data from the request
email = request.validated_data['email']
password = request.validated_data['password']
# ... process registration ...
return jsonify({"message": "User registered successfully"})
7. λ°μ΄ν° μμ
XSS λ° κΈ°ν μ μ¬μ μΈ λ³΄μ μ·¨μ½μ±μ λ°©μ§νκΈ° μν΄ λ°μ½λ μ΄ν° λ΄μμ λ°μ΄ν°λ₯Ό μμ ν©λλ€. HTML λ¬Έμλ₯Ό μΈμ½λ©νκ³ , μ μ± μ½ν μΈ λ₯Ό νν°λ§νκ³ , νΉμ λ°μ΄ν° μ ν λ° λ ΈμΆλ μ μλ μ·¨μ½μ±μ λ°λΌ λ€λ₯Έ κΈ°μ μ μ¬μ©νμμμ€.
λΌμ°νΈ 보νΈλ₯Ό μν λͺ¨λ² μ¬λ‘
- κ°λ ₯ν λΉλ° ν€ μ¬μ©: Flask μμ© νλ‘κ·Έλ¨μ `SECRET_KEY`λ 보μμ λ§€μ° μ€μν©λλ€. κ°λ ₯νκ³ λ¬΄μμ ν€λ₯Ό μμ±νκ³ μμ νκ² μ μ₯νμμμ€(μ: νκ²½ λ³μ, μ½λ 리ν¬μ§ν 리 μΈλΆμ κ΅¬μ± νμΌ). μ½λμ λΉλ° ν€λ₯Ό μ§μ νλμ½λ©νμ§ λ§μμμ€.
- λ―Όκ°ν λ°μ΄ν°μ μμ ν μ μ₯: κ°λ ₯ν ν΄μ± μκ³ λ¦¬μ¦κ³Ό μμ ν μ μ₯ λ©μ»€λμ¦μ μ¬μ©νμ¬ μνΈ λ° API ν€μ κ°μ λ―Όκ°ν λ°μ΄ν°λ₯Ό 보νΈν©λλ€. μ λ μΌλ° ν μ€νΈλ‘ μνΈλ₯Ό μ μ₯νμ§ λ§μμμ€.
- μ κΈ°μ μΈ λ³΄μ κ°μ¬: μ κΈ°μ μΈ λ³΄μ κ°μ¬ λ° μΉ¨ν¬ ν μ€νΈλ₯Ό μννμ¬ μμ© νλ‘κ·Έλ¨μ μ μ¬μ μΈ μ·¨μ½μ±μ μλ³νκ³ ν΄κ²°ν©λλ€.
- μ’ μμ± μ λ°μ΄νΈ μ μ§: 보μ ν¨μΉ λ° λ²κ·Έ μμ μ ν΄κ²°νκΈ° μν΄ Flask νλ μμν¬, λΌμ΄λΈλ¬λ¦¬ λ° μ’ μμ±μ μ κΈ°μ μΌλ‘ μ λ°μ΄νΈνμμμ€.
- HTTPS ꡬν: νμ HTTPSλ₯Ό μ¬μ©νμ¬ ν΄λΌμ΄μΈνΈμ μλ² κ°μ ν΅μ μ μνΈνν©λλ€. μ΄λ κ² νλ©΄ λμ²μ λ°©μ§νκ³ μ μ‘ μ€μΈ λ°μ΄ν°λ₯Ό 보νΈν μ μμ΅λλ€. TLS/SSL μΈμ¦μλ₯Ό ꡬμ±νκ³ HTTP νΈλν½μ HTTPSλ‘ λ¦¬λλ μ ν©λλ€.
- μ΅μ κΆν μμΉ λ°λ₯΄κΈ°: μ¬μ©μμκ² μμ μ μννλ λ° νμν μ΅μνμ κΆνλ§ λΆμ¬ν©λλ€. 리μμ€μ λν κ³Όλν μ‘μΈμ€ κΆνμ λΆμ¬νμ§ λ§μμμ€.
- λͺ¨λν°λ§ λ° λ‘κΉ : μ¬μ©μ νλμ μΆμ νκ³ , μμ¬μ€λ¬μ΄ λμμ κ°μ§νκ³ , λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ ν¬κ΄μ μΈ λ‘κΉ λ° λͺ¨λν°λ§μ ꡬνν©λλ€. μ μ¬μ μΈ λ³΄μ μ¬κ³ μ λν λ‘κ·Έλ₯Ό μ κΈ°μ μΌλ‘ κ²ν νμμμ€.
- μΉ μμ© νλ‘κ·Έλ¨ λ°©νλ²½(WAF) κ³ λ €: WAFλ μΌλ°μ μΈ μΉ κ³΅κ²©(μ: SQL μ£Όμ , ν¬λ‘μ€ μ¬μ΄νΈ μ€ν¬λ¦½ν )μΌλ‘λΆν° μμ© νλ‘κ·Έλ¨μ 보νΈνλ λ° λμμ΄ λ μ μμ΅λλ€.
- μ½λ κ²ν : μ μ¬μ μΈ λ³΄μ μ·¨μ½μ±μ μλ³νκ³ μ½λ νμ§μ 보μ₯νκΈ° μν΄ μ κΈ°μ μΈ μ½λ κ²ν λ₯Ό ꡬνν©λλ€.
- μ·¨μ½μ± μ€μΊλ μ¬μ©: κ°λ° λ° λ°°ν¬ νμ΄νλΌμΈμ μ·¨μ½μ± μ€μΊλλ₯Ό ν΅ν©νμ¬ μ½λμ μ μ¬μ μΈ λ³΄μ κ²°ν¨μ μλμΌλ‘ μλ³ν©λλ€.
μμ ν μμ© νλ‘κ·Έλ¨μ μν κΈλ‘λ² κ³ λ € μ¬ν
κΈλ‘λ² μ¬μ©μλ₯Ό μν΄ μμ© νλ‘κ·Έλ¨μ κ°λ°ν λλ 보μ λ° κ·μ μ€μμ κ΄λ ¨λ λ€μν μμλ₯Ό κ³ λ €νλ κ²μ΄ μ€μν©λλ€.
- λ°μ΄ν° κ°μΈ μ 보 λ³΄νΈ κ·μ : μ λ½μ μΌλ° λ°μ΄ν° λ³΄νΈ κ·μ (GDPR) λ° λ―Έκ΅μ μΊλ¦¬ν¬λμ μλΉμ κ°μΈ μ 보 보νΈλ²(CCPA)κ³Ό κ°μ΄ λ€μν μ§μμ κ΄λ ¨ λ°μ΄ν° κ°μΈ μ 보 λ³΄νΈ κ·μ μ μΈμνκ³ μ€μνμμμ€. μ¬κΈ°μλ μ¬μ©μ λ°μ΄ν°λ₯Ό 보νΈνκ³ , λμλ₯Ό μ»κ³ , μ¬μ©μμκ² λ°μ΄ν°μ μ‘μΈμ€, μμ λ° μμ ν μ μλ κΆνμ μ 곡νκΈ° μν μ μ ν 보μ μ‘°μΉλ₯Ό ꡬννλ κ²μ΄ ν¬ν¨λ©λλ€.
- μ§μν λ° κ΅μ ν: μμ© νλ‘κ·Έλ¨μ μ¬μ©μ μΈν°νμ΄μ€μ μ€λ₯ λ©μμ§λ₯Ό μ¬λ¬ μΈμ΄λ‘ λ²μν΄μΌ νλμ§ κ³ λ €νμμμ€. μΈμ¦ λ° κΆν λΆμ¬μ κ°μ 보μ μ‘°μΉκ° νμ§νλ μΈν°νμ΄μ€μ μ μ νκ² ν΅ν©λμλμ§ νμΈνμμμ€.
- κ·μ μ€μ: μμ© νλ‘κ·Έλ¨μ΄ νκ²ν νλ νΉμ μ°μ λλ μ§μμ κ·μ μ€μ μꡬ μ¬νμ μΆ©μ‘±νλμ§ νμΈν©λλ€. μλ₯Ό λ€μ΄ κΈμ΅ κ±°λλ₯Ό μ²λ¦¬νλ κ²½μ° PCI DSS νμ€μ μ€μν΄μΌ ν μ μμ΅λλ€.
- μκ°λ λ° λ μ§ νμ: μκ°λ λ° λ μ§ νμμ μ¬λ°λ₯΄κ² μ²λ¦¬ν©λλ€. λΆμΌμΉλ μΌμ , λ°μ΄ν° λΆμ λ° κ·μ μ€μμ μ€λ₯λ₯Ό λ°μμν¬ μ μμ΅λλ€. νμμ€ν¬νλ₯Ό UTC νμμΌλ‘ μ μ₯νκ³ νμλ₯Ό μν΄ μ¬μ©μμ νμ§ μκ°λλ‘ λ³ννλ κ²μ κ³ λ €νμμμ€.
- λ¬Ένμ λ―Όκ°μ±: μμ© νλ‘κ·Έλ¨μμ 곡격μ μ΄κ±°λ λ¬Ένμ μΌλ‘ λΆμ μ ν μΈμ΄ λλ μ΄λ―Έμ§λ₯Ό μ¬μ©νμ§ λ§μμμ€. 보μ κ΄νκ³Ό κ΄λ ¨νμ¬ λ¬Ένμ μ°¨μ΄μ μ μΌλμ λμμμ€. μλ₯Ό λ€μ΄ ν κ΅κ°μμλ νν κ°λ ₯ν μνΈ μ μ± μ΄ λ€λ₯Έ κ΅κ°μμλ λ무 μ νμ μΈ κ²μΌλ‘ κ°μ£Όλ μ μμ΅λλ€.
- λ²μ μꡬ μ¬ν: μ΄μνλ μ¬λ¬ κ΅κ°μ λ²μ μꡬ μ¬νμ μ€μν©λλ€. μ¬κΈ°μλ λ°μ΄ν° μ μ₯, λμ λ° μ¬μ©μ λ°μ΄ν° μ²λ¦¬κ° ν¬ν¨λ μ μμ΅λλ€.
- κ²°μ μ²λ¦¬: μμ© νλ‘κ·Έλ¨μμ κ²°μ λ₯Ό μ²λ¦¬νλ κ²½μ° νμ§ κ²°μ μ²λ¦¬ κ·μ μ μ€μνκ³ λ€μν ν΅νλ₯Ό μ§μνλ μμ ν κ²°μ κ²μ΄νΈμ¨μ΄λ₯Ό μ¬μ©ν΄μΌ ν©λλ€. λ€μν κ΅κ° λ° λ¬Ένμμ λ€μν κ²°μ λ°©λ²μ μ¬μ©νλ―λ‘ νμ§ κ²°μ μ΅μ μ κ³ λ €νμμμ€.
- λ°μ΄ν° μμ£Ό: μΌλΆ κ΅κ°μμλ νΉμ μ νμ λ°μ΄ν°λ₯Ό ν΄λΉ κ΅κ²½ λ΄μ μ μ₯νλλ‘ μꡬνλ κ·μ μ΄ μμ μ μμ΅λλ€. νΉμ μ§μμ λ°μ΄ν° μΌν°λ₯Ό μ 곡νλ νΈμ€ν μ 곡μ 체λ₯Ό μ νν΄μΌ ν μ μμ΅λλ€.
- μ κ·Όμ±: WCAG μ§μΉ¨μ λ°λΌ μ₯μ κ° μλ μ¬μ©μκ° μμ© νλ‘κ·Έλ¨μ μ‘μΈμ€ν μ μλλ‘ ν©λλ€. μ κ·Όμ±μ κΈλ‘λ² κ΄μ¬μ¬μ΄λ©° μ¬μ©μμ μ 체μ λλ μΈμ§μ λ₯λ ₯μ κ΄κ³μμ΄ μ¬μ©μμκ² λλ±ν μ κ·Όμ μ 곡νλ κΈ°λ³Έμ μΈ μꡬ μ¬νμ λλ€.
κ²°λ‘
μ¬μ©μ μ μ λ°μ½λ μ΄ν°λ Flask μμ© νλ‘κ·Έλ¨μμ λΌμ°νΈ 보νΈλ₯Ό ꡬννλ κ°λ ₯νκ³ μ°μν μ κ·Ό λ°©μμ μ 곡ν©λλ€. μΈμ¦ λ° κΆν λΆμ¬ λ°μ½λ μ΄ν°λ₯Ό μ¬μ©νμ¬ μμ νκ³ κ°λ ₯ν API λ° μΉ μΈν°νμ΄μ€λ₯Ό ꡬμΆν μ μμ΅λλ€. λͺ¨λ² μ¬λ‘λ₯Ό λ°λ₯΄κ³ , ν¬κ΄μ μΈ μ€λ₯ μ²λ¦¬λ₯Ό ꡬννκ³ , κΈλ‘λ² μ¬μ©μλ₯Ό μν΄ μμ© νλ‘κ·Έλ¨μ κ°λ°ν λ κΈλ‘λ² μμλ₯Ό κ³ λ €νμμμ€. 보μμ μ°μ μνκ³ μ κ³ νμ€μ μ€μν¨μΌλ‘μ¨ μ μΈκ³ μ¬μ©μκ° μ λ’°νλ μμ© νλ‘κ·Έλ¨μ ꡬμΆν μ μμ΅λλ€.
μ 곡λ μλ νμμ μΈ κ°λ μ 보μ¬μ€λλ€. μ€μ ꡬνμ νΉν νλ‘λμ νκ²½μμ λ 볡μ‘ν μ μμ΅λλ€. μΈλΆ μλΉμ€, λ°μ΄ν°λ² μ΄μ€ λ° κ³ κΈ λ³΄μ κΈ°λ₯κ³Όμ ν΅ν©μ κ³ λ €νμμμ€. λμμλ νμ΅κ³Ό μ μμ μΉ λ³΄μμ μ§ννλ νκ²½μμ νμμ μ λλ€. μ κΈ°μ μΈ ν μ€νΈ, 보μ κ°μ¬ λ° μ΅μ 보μ λͺ¨λ² μ¬λ‘ μ€μλ μμ ν μμ© νλ‘κ·Έλ¨μ μ μ§νλ λ° λ§€μ° μ€μν©λλ€.